{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ISubpr_SSsiM"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "3jTMb1dySr3V"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6DWfyNThSziV"
      },
      "source": [
        "# モジュール、レイヤー、モデルの概要\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>     <a target=\"_blank\" href=\"https://www.tensorflow.org/guide/intro_to_modules\" class=\"\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org で表示</a>   </td>\n",
        "  <td>     <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/guide/intro_to_modules.ipynb\" class=\"\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab で実行</a>   </td>\n",
        "  <td>     <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/guide/intro_to_modules.ipynb\" class=\"\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示</a>   </td>\n",
        "  <td>     <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/guide/intro_to_modules.ipynb\" class=\"\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード</a>   </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "v0DdlfacAdTZ"
      },
      "source": [
        "TensorFlow で機械学習を実行するには、モデルを定義、保存、復元する必要があります。\n",
        "\n",
        "モデルは、抽象的に次のように定義できます。\n",
        "\n",
        "- テンソルで何かを計算する関数（**フォワードパス**）\n",
        "- トレーニングに応じて更新できる何らかの変数\n",
        "\n",
        "このガイドでは、Keras 内で TensorFlow モデルがどのように定義されているか、そして、TensorFlow が変数とモデルを収集する方法、および、モデルを保存および復元する方法を説明します。\n",
        "\n",
        "注意：今すぐ Keras を使用するのであれば、[一連の Keras ガイド](./keras/)をご覧ください。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VSa6ayJmfZxZ"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "goZwOXp_xyQj"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "from datetime import datetime\n",
        "\n",
        "%load_ext tensorboard"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yt5HEbsYAbw1"
      },
      "source": [
        "## TensorFlow におけるモデルとレイヤーの定義\n",
        "\n",
        "ほとんどのモデルはレイヤーで構成されています。レイヤーは、再利用およびトレーニング可能な変数を持つ既知の数学的構造を持つ関数です。TensorFlow では、Keras や Sonnet といった、レイヤーとモデルの高位実装の多くは、同じ基本クラスの `tf.Module` に基づいて構築されています。\n",
        "\n",
        "スカラーテンソルで動作する非常に単純な `tf.Module` の例を次に示します。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "alhYPVEtAiSy"
      },
      "outputs": [],
      "source": [
        "class SimpleModule(tf.Module):\n",
        "  def __init__(self, name=None):\n",
        "    super().__init__(name=name)\n",
        "    self.a_variable = tf.Variable(5.0, name=\"train_me\")\n",
        "    self.non_trainable_variable = tf.Variable(5.0, trainable=False, name=\"do_not_train_me\")\n",
        "  def __call__(self, x):\n",
        "    return self.a_variable * x + self.non_trainable_variable\n",
        "\n",
        "simple_module = SimpleModule(name=\"simple\")\n",
        "\n",
        "simple_module(tf.constant(5.0))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JwMc_zu5Ant8"
      },
      "source": [
        "モジュールと（その延長としての）レイヤーは、「オブジェクト」のディープラーニング用語です。これらには、内部状態と、その状態を使用するメソッドがあります。\n",
        "\n",
        "`__ call__` は [Python コーラブル](https://stackoverflow.com/questions/111234/what-is-a-callable)のように動作する以外何も特別なことではないため、任意の関数を使用してモデルを呼び出すことができます。\n",
        "\n",
        "ファインチューニング中のレイヤーと変数を凍結するなど、様々な理由で、変数をトレーニング対象とするかどうかを設定することができます。\n",
        "\n",
        "注意: `tf.Module` は `tf.keras.layers.Layer` と `tf.keras.Model` のの基本クラスであるため、ここに説明されているすべての内容は Keras にも当てはまります。過去の互換性の理由から、Keras レイヤーはモジュールから変数を収集しないため、モデルはモジュールのみ、または Keras レイヤーのみを使用する必要があります。ただし、以下に示す変数の検査方法はどちらの場合も同じです。\n",
        "\n",
        "`tf.Module` をサブクラス化することにより、このオブジェクトのプロパティに割り当てられた `tf.Variable` または `tf.Module` インスタンスが自動的に収集されます。これにより、変数の保存や読み込みのほか、`tf.Module` のコレクションを作成することができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CyzYy4A_CbVf"
      },
      "outputs": [],
      "source": [
        "# All trainable variables\n",
        "print(\"trainable variables:\", simple_module.trainable_variables)\n",
        "# Every variable\n",
        "print(\"all variables:\", simple_module.variables)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nuSFrRUNCaaW"
      },
      "source": [
        "これは、モジュールで構成された 2 層線形レイヤーモデルの例です。\n",
        "\n",
        "最初の高密度（線形）レイヤーは以下のとおりです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Efb2p2bzAn-V"
      },
      "outputs": [],
      "source": [
        "class Dense(tf.Module):\n",
        "  def __init__(self, in_features, out_features, name=None):\n",
        "    super().__init__(name=name)\n",
        "    self.w = tf.Variable(\n",
        "      tf.random.normal([in_features, out_features]), name='w')\n",
        "    self.b = tf.Variable(tf.zeros([out_features]), name='b')\n",
        "  def __call__(self, x):\n",
        "    y = tf.matmul(x, self.w) + self.b\n",
        "    return tf.nn.relu(y)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bAhMuC-UpnhX"
      },
      "source": [
        "2 つのレイヤーインスタンスを作成して適用する完全なモデルは以下のとおりです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QQ7qQf-DFw74"
      },
      "outputs": [],
      "source": [
        "class SequentialModule(tf.Module):\n",
        "  def __init__(self, name=None):\n",
        "    super().__init__(name=name)\n",
        "\n",
        "    self.dense_1 = Dense(in_features=3, out_features=3)\n",
        "    self.dense_2 = Dense(in_features=3, out_features=2)\n",
        "\n",
        "  def __call__(self, x):\n",
        "    x = self.dense_1(x)\n",
        "    return self.dense_2(x)\n",
        "\n",
        "# You have made a model!\n",
        "my_model = SequentialModule(name=\"the_model\")\n",
        "\n",
        "# Call it, with random results\n",
        "print(\"Model results:\", my_model(tf.constant([[2.0, 2.0, 2.0]])))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d1oUzasJHHXf"
      },
      "source": [
        "`tf.Module` インスタンスは、それに割り当てられた `tf.Variable` または `tf.Module` インスタンスを再帰的に自動収集します。これにより、単一のモデルインスタンスで `tf.Module` のコレクションを管理し、モデル全体を保存して読み込むことができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JLFA5_PEGb6C"
      },
      "outputs": [],
      "source": [
        "print(\"Submodules:\", my_model.submodules)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6lzoB8pcRN12"
      },
      "outputs": [],
      "source": [
        "for var in my_model.variables:\n",
        "  print(var, \"\\n\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hoaxL3zzm0vK"
      },
      "source": [
        "### 変数の作成を延期する\n",
        "\n",
        "ここで、レイヤーへの入力サイズと出力サイズの両方を定義する必要があることに気付いたかもしれません。これは、`w` 変数が既知の形状を持ち、割り当てることができるようにするためです。\n",
        "\n",
        "モジュールが特定の入力形状で最初に呼び出されるまで変数の作成を延期することにより、入力サイズを事前に指定する必要がありません。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "XsGCLFXlnPum"
      },
      "outputs": [],
      "source": [
        "class FlexibleDenseModule(tf.Module):\n",
        "  # Note: No need for `in_features`\n",
        "  def __init__(self, out_features, name=None):\n",
        "    super().__init__(name=name)\n",
        "    self.is_built = False\n",
        "    self.out_features = out_features\n",
        "\n",
        "  def __call__(self, x):\n",
        "    # Create variables on first call.\n",
        "    if not self.is_built:\n",
        "      self.w = tf.Variable(\n",
        "        tf.random.normal([x.shape[-1], self.out_features]), name='w')\n",
        "      self.b = tf.Variable(tf.zeros([self.out_features]), name='b')\n",
        "      self.is_built = True\n",
        "\n",
        "    y = tf.matmul(x, self.w) + self.b\n",
        "    return tf.nn.relu(y)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8bjOWax9LOkP"
      },
      "outputs": [],
      "source": [
        "# Used in a module\n",
        "class MySequentialModule(tf.Module):\n",
        "  def __init__(self, name=None):\n",
        "    super().__init__(name=name)\n",
        "\n",
        "    self.dense_1 = FlexibleDenseModule(out_features=3)\n",
        "    self.dense_2 = FlexibleDenseModule(out_features=2)\n",
        "\n",
        "  def __call__(self, x):\n",
        "    x = self.dense_1(x)\n",
        "    return self.dense_2(x)\n",
        "\n",
        "my_model = MySequentialModule(name=\"the_model\")\n",
        "print(\"Model results:\", my_model(tf.constant([[2.0, 2.0, 2.0]])))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "49JfbhVrpOLH"
      },
      "source": [
        "この柔軟性のため、多くの場合、TensorFlow レイヤーは、出力の形状（`tf.keras.layers.Dense`）などを指定するだけで済みます。入出力サイズの両方を指定する必要はありません。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JOLVVBT8J_dl"
      },
      "source": [
        "## 重みを保存する\n",
        "\n",
        "`tf.Module` は[チェックポイント](./checkpoint.ipynb)と [SavedModel](./saved_model.ipynb) の両方として保存できます。\n",
        "\n",
        "チェックポイントは単なる重み（モジュールとそのサブモジュール内の変数のセットの値）です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pHXKRDk7OLHA"
      },
      "outputs": [],
      "source": [
        "chkp_path = \"my_checkpoint\"\n",
        "checkpoint = tf.train.Checkpoint(model=my_model)\n",
        "checkpoint.write(chkp_path)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WXOPMBR4T4ZR"
      },
      "source": [
        "チェックポイントは、データ自体とメタデータのインデックスファイルの 2 種類のファイルで構成されます。インデックスファイルは、実際に保存されているものとチェックポイントの番号を追跡し、チェックポイントデータには変数値とその属性ルックアップパスが含まれています。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jBV3fprlTWqJ"
      },
      "outputs": [],
      "source": [
        "!ls my_checkpoint*"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CowCuBTvXgUu"
      },
      "source": [
        "チェックポイントの内部を調べると、変数のコレクション全体が保存されており、変数を含む Python オブジェクト別に並べ替えられていることを確認できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "o2QAdfpvS8tB"
      },
      "outputs": [],
      "source": [
        "tf.train.list_variables(chkp_path)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4eGaNiQWcK4j"
      },
      "source": [
        "分散（マルチマシン）トレーニング中にシャーディングされる可能性があるため、番号が付けられています（「00000-of-00001」など）。ただし、この例の場合、シャードは 1 つしかありません。\n",
        "\n",
        "モデルを再度読み込むと、Python オブジェクトの値が上書きされます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "UV8rdDzcwVVg"
      },
      "outputs": [],
      "source": [
        "new_model = MySequentialModule()\n",
        "new_checkpoint = tf.train.Checkpoint(model=new_model)\n",
        "new_checkpoint.restore(\"my_checkpoint\")\n",
        "\n",
        "# Should be the same result as above\n",
        "new_model(tf.constant([[2.0, 2.0, 2.0]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BnPwDRwamdfq"
      },
      "source": [
        "注意: チェックポイントは長いトレーニングワークフローでは重要であり、`tf.checkpoint.CheckpointManager` はヘルパークラスとして、チェックポイント管理を大幅に簡単にすることができます。詳細については、[トレーニングチェックポイントガイド](./checkpoint.ipynb)をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pSZebVuWxDXu"
      },
      "source": [
        "## 関数の保存\n",
        "\n",
        "TensorFlow は、[TensorFlow Serving](https://tensorflow.org/tfx) と [TensorFlow Lite](https://tensorflow.org/lite) で見たように、元の Python オブジェクトなしでモデルを実行できます。また、[TensorFlow Hub](https://tensorflow.org/hub) からトレーニング済みのモデルをダウンロードした場合でも同じです。\n",
        "\n",
        "TensorFlow は、Pythonで説明されている計算の実行方法を認識する必要がありますが、**元のコードは必要ありません**。認識させるには、**グラフ**を作成することができます。これについては[グラフと関数の入門ガイド](./intro_to_graphs.ipynb)をご覧ください。\n",
        "\n",
        "このグラフには、関数を実装する*演算*が含まれています。\n",
        "\n",
        "`@tf.function` デコレータを追加して、このコードをグラフとして実行する必要があることを示すことにより、上記のモデルでグラフを定義できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WQTvkapUh7lk"
      },
      "outputs": [],
      "source": [
        "class MySequentialModule(tf.Module):\n",
        "  def __init__(self, name=None):\n",
        "    super().__init__(name=name)\n",
        "\n",
        "    self.dense_1 = Dense(in_features=3, out_features=3)\n",
        "    self.dense_2 = Dense(in_features=3, out_features=2)\n",
        "\n",
        "  @tf.function\n",
        "  def __call__(self, x):\n",
        "    x = self.dense_1(x)\n",
        "    return self.dense_2(x)\n",
        "\n",
        "# You have made a model with a graph!\n",
        "my_model = MySequentialModule(name=\"the_model\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hW66YXBziLo9"
      },
      "source": [
        "作成したモジュールは、前と全く同じように動作します。関数に渡される一意のシグネチャごとにグラフが作成されます。詳細については、[グラフと関数の基礎ガイド](./intro_to_graphs.ipynb)をご覧ください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "H5zUfti3iR52"
      },
      "outputs": [],
      "source": [
        "print(my_model([[2.0, 2.0, 2.0]]))\n",
        "print(my_model([[[2.0, 2.0, 2.0], [2.0, 2.0, 2.0]]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lbGlU1kgyDo7"
      },
      "source": [
        "TensorBoard のサマリー内でグラフをトレースすると、グラフを視覚化できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zmy-T67zhp-S"
      },
      "outputs": [],
      "source": [
        "# Set up logging.\n",
        "stamp = datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n",
        "logdir = \"logs/func/%s\" % stamp\n",
        "writer = tf.summary.create_file_writer(logdir)\n",
        "\n",
        "# Create a new model to get a fresh trace\n",
        "# Otherwise the summary will not see the graph.\n",
        "new_model = MySequentialModule()\n",
        "\n",
        "# Bracket the function call with\n",
        "# tf.summary.trace_on() and tf.summary.trace_export().\n",
        "tf.summary.trace_on(graph=True)\n",
        "tf.profiler.experimental.start(logdir)\n",
        "# Call only one tf.function when tracing.\n",
        "z = print(new_model(tf.constant([[2.0, 2.0, 2.0]])))\n",
        "with writer.as_default():\n",
        "  tf.summary.trace_export(\n",
        "      name=\"my_func_trace\",\n",
        "      step=0,\n",
        "      profiler_outdir=logdir)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gz4lwNZ9hR79"
      },
      "source": [
        "TensorBoard を起動して、トレースの結果を確認します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "V4MXDbgBnkJu"
      },
      "outputs": [],
      "source": [
        "#docs_infra: no_execute\n",
        "%tensorboard --logdir logs/func"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Gjattu0AhYUl"
      },
      "source": [
        "![A screenshot of the graph, in tensorboard](https://github.com/tensorflow/docs-l10n/blob/master/site/ja/guide/images/tensorboard_graph.png?raw=true)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SQu3TVZecmL7"
      },
      "source": [
        "### `SavedModel` の作成\n",
        "\n",
        "トレーニングが完了したモデルを共有するには、`SavedModel` の使用が推奨されます。`SavedModel` には関数のコレクションと重みのコレクションの両方が含まれています。\n",
        "\n",
        "次のようにして、トレーニングしたモデルを保存することができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Awv_Tw__WK7a"
      },
      "outputs": [],
      "source": [
        "tf.saved_model.save(my_model, \"the_saved_model\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SXv3mEKsefGj"
      },
      "outputs": [],
      "source": [
        "# Inspect the SavedModel in the directory\n",
        "!ls -l the_saved_model"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vQQ3hEvHYdoR"
      },
      "outputs": [],
      "source": [
        "# The variables/ directory contains a checkpoint of the variables \n",
        "!ls -l the_saved_model/variables"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xBqPop7ZesBU"
      },
      "source": [
        "`saved_model.pb` ファイルは、関数型の `tf.Graph` を記述する[プロトコルバッファ](https://developers.google.com/protocol-buffers)です。\n",
        "\n",
        "モデルとレイヤーは、それを作成したクラスのインスタンスを実際に作成しなくても、この表現から読み込めます。これは、大規模なサービスやエッジデバイスでのサービスなど、Python インタープリタがない（または使用しない）場合や、元の Python コードが利用できないか実用的でない場合に有用です。\n",
        "\n",
        "モデルを新しいオブジェクトとして読み込みます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zRFcA5wIefv4"
      },
      "outputs": [],
      "source": [
        "new_model = tf.saved_model.load(\"the_saved_model\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-9EF3mT7i3qN"
      },
      "source": [
        "保存したモデルを読み込んで作成された `new_model` は、クラスを認識しない内部の TensorFlow ユーザーオブジェクトです。`SequentialModule` ではありません。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EC_eQj7yi54G"
      },
      "outputs": [],
      "source": [
        "isinstance(new_model, SequentialModule)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-OrOX1zxiyhR"
      },
      "source": [
        "この新しいモデルは、すでに定義されている入力シグネチャで機能します。このように復元されたモデルにシグネチャを追加することはできません。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_23BYYBWfKnc"
      },
      "outputs": [],
      "source": [
        "print(my_model([[2.0, 2.0, 2.0]]))\n",
        "print(my_model([[[2.0, 2.0, 2.0], [2.0, 2.0, 2.0]]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qSFhoMtTjSR6"
      },
      "source": [
        "したがって、`SavedModel` を使用すると、`tf.Module` を使用して TensorFlow の重みとグラフを保存し、それらを再度読み込むことができます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Rb9IdN7hlUZK"
      },
      "source": [
        "## Keras モデルとレイヤー\n",
        "\n",
        "ここまでは、Keras に触れずに説明してきましたが、`tf.Module` の上に独自の高位 API を構築することは可能です。\n",
        "\n",
        "このセクションでは、Keras が `tf.Module` をどのように使用するかを説明します。Keras モデルの完全なユーザーガイドは、[Keras ガイド](keras/sequential_model.ipynb)をご覧ください。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uigsVGPreE-D"
      },
      "source": [
        "### Keras レイヤー\n",
        "\n",
        "`tf.keras.layers.Layer` はすべての Keras レイヤーの基本クラスであり、`tf.Module` から継承します。\n",
        "\n",
        "親を交換してから、`__call__` を `call` に変更するだけで、モジュールを Keras レイヤーに変換できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "88YOGquhnQRd"
      },
      "outputs": [],
      "source": [
        "class MyDense(tf.keras.layers.Layer):\n",
        "  # Adding **kwargs to support base Keras layer arguments\n",
        "  def __init__(self, in_features, out_features, **kwargs):\n",
        "    super().__init__(**kwargs)\n",
        "\n",
        "    # This will soon move to the build step; see below\n",
        "    self.w = tf.Variable(\n",
        "      tf.random.normal([in_features, out_features]), name='w')\n",
        "    self.b = tf.Variable(tf.zeros([out_features]), name='b')\n",
        "  def call(self, x):\n",
        "    y = tf.matmul(x, self.w) + self.b\n",
        "    return tf.nn.relu(y)\n",
        "\n",
        "simple_layer = MyDense(name=\"simple\", in_features=3, out_features=3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nYGmAsPrws--"
      },
      "source": [
        "Keras レイヤーには独自の `__call__` があり、次のセクションで説明する手順を実行してから、`call()` を呼び出します。動作には違いはありません。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nIqE8wOznYKG"
      },
      "outputs": [],
      "source": [
        "simple_layer([[2.0, 2.0, 2.0]])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tmN5vb1K18U1"
      },
      "source": [
        "### `build` ステップ\n",
        "\n",
        "前述のように、多くの場合都合よく、入力形状が確定するまで変数の作成を延期できます。\n",
        "\n",
        "Keras レイヤーには追加のライフサイクルステップがあり、レイヤーをより柔軟に定義することができます。このステップは、`build()` 関数で定義されます。\n",
        "\n",
        "`build` は 1 回だけ呼び出され、入力形状で呼び出されます。通常、変数（重み）を作成するために使用されます。\n",
        "\n",
        "上記の `MyDense` レイヤーを、入力のサイズに柔軟に合わせられるように書き換えることができます。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4YTfrlgdsURp"
      },
      "outputs": [],
      "source": [
        "class FlexibleDense(tf.keras.layers.Layer):\n",
        "  # Note the added `**kwargs`, as Keras supports many arguments\n",
        "  def __init__(self, out_features, **kwargs):\n",
        "    super().__init__(**kwargs)\n",
        "    self.out_features = out_features\n",
        "\n",
        "  def build(self, input_shape):  # Create the state of the layer (weights)\n",
        "    self.w = tf.Variable(\n",
        "      tf.random.normal([input_shape[-1], self.out_features]), name='w')\n",
        "    self.b = tf.Variable(tf.zeros([self.out_features]), name='b')\n",
        "\n",
        "  def call(self, inputs):  # Defines the computation from inputs to outputs\n",
        "    return tf.matmul(inputs, self.w) + self.b\n",
        "\n",
        "# Create the instance of the layer\n",
        "flexible_dense = FlexibleDense(out_features=3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Koc_uSqt2PRh"
      },
      "source": [
        "この時点では、モデルは構築されていないため、変数も存在しません。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DgyTyUD32Ln4"
      },
      "outputs": [],
      "source": [
        "flexible_dense.variables"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-KdamIVl2W8Y"
      },
      "source": [
        "関数を呼び出すと、適切なサイズの変数が割り当てられます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IkLyEx7uAoTK"
      },
      "outputs": [],
      "source": [
        "# Call it, with predictably random results\n",
        "print(\"Model results:\", flexible_dense(tf.constant([[2.0, 2.0, 2.0], [3.0, 3.0, 3.0]])))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Swofpkrd2YDd"
      },
      "outputs": [],
      "source": [
        "flexible_dense.variables"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7PuNUnf0OIpF"
      },
      "source": [
        "`build`は 1 回しか呼び出されないため、入力形状がレイヤーの変数と互換性がない場合、入力は拒否されます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "caYWDrHSAy_j"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "  print(\"Model results:\", flexible_dense(tf.constant([[2.0, 2.0, 2.0, 2.0]])))\n",
        "except tf.errors.InvalidArgumentError as e:\n",
        "  print(\"Failed:\", e)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YnporXiudF1I"
      },
      "source": [
        "Keras レイヤーには、次のような多くの追加機能があります。\n",
        "\n",
        "- オプションの損失\n",
        "- メトリクスのサポート\n",
        "- トレーニングと推論の使用を区別する、オプションの `training` 引数の組み込みサポート\n",
        "- Python でモデルのクローンを作成するための構成を正確に保存する `get_config` と <code>from_config</code> メソッド\n",
        "\n",
        "詳細は、カスタムレイヤーとモデルに関する[完全ガイド](./keras/custom_layers_and_models.ipynb)をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "L2kds2IHw2KD"
      },
      "source": [
        "### Keras モデル\n",
        "\n",
        "モデルはネストされた Keras レイヤーとして定義できます。\n",
        "\n",
        "ただし、Keras は `tf.keras.Model` と呼ばれるフル機能のモデルクラスも提供します。Keras モデルは `tf.keras.layers.Layer` を継承しているため、 Keras レイヤーと同じ方法で使用、ネスト、保存することができます。Keras モデルには、トレーニング、評価、読み込み、保存、および複数のマシンでのトレーニングを容易にする追加機能があります。\n",
        "\n",
        "上記の `SequentialModule` をほぼ同じコードで定義できます。先ほどと同じように、`__call__` を`call()` に変換して、親を変更します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Hqjo1DiyrHrn"
      },
      "outputs": [],
      "source": [
        "class MySequentialModel(tf.keras.Model):\n",
        "  def __init__(self, name=None, **kwargs):\n",
        "    super().__init__(**kwargs)\n",
        "\n",
        "    self.dense_1 = FlexibleDense(out_features=3)\n",
        "    self.dense_2 = FlexibleDense(out_features=2)\n",
        "  def call(self, x):\n",
        "    x = self.dense_1(x)\n",
        "    return self.dense_2(x)\n",
        "\n",
        "# You have made a Keras model!\n",
        "my_sequential_model = MySequentialModel(name=\"the_model\")\n",
        "\n",
        "# Call it on a tensor, with random results\n",
        "print(\"Model results:\", my_sequential_model(tf.constant([[2.0, 2.0, 2.0]])))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8i-CR_h2xw3z"
      },
      "source": [
        "追跡変数やサブモジュールなど、すべて同じ機能を利用できます。\n",
        "\n",
        "注意: 上記の「注意」を繰り返すと、Keras レイヤーまたはモデル内にネストされた生の `tf.Module` は、トレーニングまたは保存のために変数を収集しません。代わりに、Keras レイヤーを Keras レイヤーの内側にネストします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "hdLQFNdMsOz1"
      },
      "outputs": [],
      "source": [
        "my_sequential_model.variables"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JjVAMrAJsQ7G"
      },
      "outputs": [],
      "source": [
        "my_sequential_model.submodules"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FhP8EItC4oac"
      },
      "source": [
        "非常に Python 的なアプローチとして、`tf.keras.Model` をオーバーライドして TensorFlow モデルを構築することができます。ほかのフレームワークからモデルを移行する場合、これは非常に簡単な方法です。\n",
        "\n",
        "モデルが既存のレイヤーと入力の単純な集合として構築されている場合は、モデルの再構築とアーキテクチャに関する追加機能を備えた [Functional API](./keras/functional.ipynb) を使用すると手間とスペースを節約できます。\n",
        "\n",
        "以下は、Functional API を使用した同じモデルです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jJiZZiJ0fyqQ"
      },
      "outputs": [],
      "source": [
        "inputs = tf.keras.Input(shape=[3,])\n",
        "\n",
        "x = FlexibleDense(3)(inputs)\n",
        "x = FlexibleDense(2)(x)\n",
        "\n",
        "my_functional_model = tf.keras.Model(inputs=inputs, outputs=x)\n",
        "\n",
        "my_functional_model.summary()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kg-xAZw5gaG6"
      },
      "outputs": [],
      "source": [
        "my_functional_model(tf.constant([[2.0, 2.0, 2.0]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "s_BK9XH5q9cq"
      },
      "source": [
        "ここでの主な違いは、入力形状が関数構築プロセスの一部として事前に指定されることです。この場合、`input_shape` 引数を完全に指定する必要がないため、一部の次元を `None` のままにしておくことができます。\n",
        "\n",
        "注意：サブクラス化されたモデルでは、`input_shape` や `InputLayer` を指定する必要はありません。これらの引数とレイヤーは無視されます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qI9aXLnaHEFF"
      },
      "source": [
        "## Keras モデルの保存\n",
        "\n",
        "Keras モデルでは `tf.Module`と同じようにチェックポイントを設定できます。\n",
        "\n",
        "Keras モデルはモジュールであるため、`tf.saved_models.save()` を使用して保存することもできます。ただし、Keras モデルには便利なメソッドやその他の機能があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SAz-KVZlzAJu"
      },
      "outputs": [],
      "source": [
        "my_sequential_model.save(\"exname_of_file\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "C2urAeR-omns"
      },
      "source": [
        "このように簡単に、読み込み直すことができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Wj5DW-LCopry"
      },
      "outputs": [],
      "source": [
        "reconstructed_model = tf.keras.models.load_model(\"exname_of_file\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EA7P_MNvpviZ"
      },
      "source": [
        "また、Keras `SavedModel` は、メトリクス、損失、およびオプティマイザの状態も保存します。\n",
        "\n",
        "再構築されたこのモデルを使用すると、同じデータで呼び出されたときと同じ結果が得られます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "P_wGfQo5pe6T"
      },
      "outputs": [],
      "source": [
        "reconstructed_model(tf.constant([[2.0, 2.0, 2.0]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xKyjlkceqjwD"
      },
      "source": [
        "機能サポートのためにカスタムレイヤーで使用できる構成メソッドなど、Keras モデルの保存とシリアル化についてのその他の詳細情報は、[保存とシリアル化のガイド](keras/save_and_serialize)をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kcdMMPYv7Krz"
      },
      "source": [
        "# 次のステップ\n",
        "\n",
        "Keras の詳細については、[こちら](./keras/)から既存の Keras ガイドをご覧ください。\n",
        "\n",
        "`tf.module` 上に構築された高位 API の例には、DeepMind の Sonnet も利用できます。詳細については[ウェブサイト](https://github.com/deepmind/sonnet)をご覧ください。"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [
        "ISubpr_SSsiM"
      ],
      "name": "intro_to_modules.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
